package tools

import (
	"fmt"
	"io"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"runtime/pprof"
	"syscall"
	"time"
)

var pathProfile = "./"

type ProfileConfig struct {
	Path      string
	WebPort   string
	EnableCPU bool
	EnableMem bool
	MemStamp  int64 //ms
}

func StartProfile(cfg *ProfileConfig) error {
	if cfg == nil {
		return nil
	}
	pathProfile = cfg.Path
	ec := NewErrCatcher()
	if cfg.WebPort != "" {
		go func() {
			http.ListenAndServe(cfg.WebPort, nil)
		}()
		return nil
	}
	if cfg.EnableCPU {
		ec.Execute(func() error {
			return CPUProfileNow()
		})
	}
	if cfg.EnableMem {
		if cfg.MemStamp <= 0 {
			cfg.MemStamp = 3600000 //default stamp
		}
		ticker := time.NewTicker(time.Duration(cfg.MemStamp) * time.Millisecond)
		MemProfileNow()
		go func() {
			for range ticker.C {
				MemProfileNow()
			}
		}()
	}
	return ec.Err
}

func CPUProfileNow() error {
	path := pathProfile + "cpu.profile." + time.Now().Format("200601021504")
	return CPUProfileF(path)
}

func CPUProfileF(path string) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	// defer f.Close() // Close will lose data, it will close when stop
	return CPUProfile(f)
}

//CPUProfile start a cpu profile on w until program exit
func CPUProfile(w io.Writer) error {
	// 开始cpu profile，结果写到文件f中
	if err := pprof.StartCPUProfile(w); err != nil {
		return err
	}
	signalChan := make(chan os.Signal, 1)
	go func() {
		//阻塞程序运行，直到收到终止的信号
		<-signalChan
		pprof.StopCPUProfile() // 结束profile
		os.Exit(0)
	}()
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
	return nil
}

//MemProfileNow create MemProfile use a default file
func MemProfileNow() error {
	path := pathProfile + "mem.profile." + time.Now().Format("200601021504")
	return MemProfileF(path)
}

//MemProfileF create MemProfile use file at path
func MemProfileF(path string) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()
	return MemProfile(f)
}

//MemProfile is shorthand of pprof.WriteHeapProfile(w)
//write current mem profile
func MemProfile(w io.Writer) error {
	p := pprof.Lookup("heap")
	if p == nil {
		return fmt.Errorf("MemProfile nil heap profile")
	}
	return p.WriteTo(w, 1)
	// return pprof.WriteHeapProfile(w)
}
